package com.gitee.wsl.struct.grammar.parser.context.ext

import com.gitee.wsl.struct.grammar.parser.Parser
import com.gitee.wsl.struct.grammar.parser.ParsingScope
import com.gitee.wsl.struct.grammar.parser.result.NoViableAlternative
import com.gitee.wsl.struct.grammar.parser.result.ParsedValue


/**
 * Returns the result of [the first parser][p1] if parsing succeeds,
 * otherwise returns the result of [the second parser][p2].
 */
suspend fun <R> ParsingScope.choose(p1: Parser<R>, p2: Parser<R>): R {
    val startOffset = position
    var r = tryParse(p1)
    if (r is ParsedValue) return r.value

    r = tryParse(p2)
    if (r is ParsedValue) return r.value
    fail(NoViableAlternative(startOffset))
}

suspend fun <R> ParsingScope.choose(p: Parser<R>, vararg ps: Parser<R>): R = choose(p, ps.toList())

suspend fun <R> ParsingScope.choose(p: Parser<R>, ps: List<Parser<R>>): R {
    if (ps.isEmpty()) return p()

    val startOffset = this.position
    for (i in -1..ps.lastIndex) {
        val alt = if (i == -1) p else ps[i]
        val r = tryParse(alt)
        if (r is ParsedValue) return r.value
    }

    fail(NoViableAlternative(startOffset))
}